// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

namespace SnapObjects.Data
{
    internal class SqlFromBuilder : SqlBuilderComb, ISqlFromBuilder
    {
        protected SqlBuildClause _clause;
        protected ISqlJoinBuilder _join;

        public SqlFromBuilder(
            ISqlBuilder builder,
            SqlBuildClause from,
            SqlBuildClause join,
            SqlBuildClause where,
            SqlBuildClause group,
            SqlBuildClause having,
            SqlBuildClause order) : base(builder)
        {
            _clause = from;
            _join = new SqlJoinBuilder(builder, join, where, group, having, order);
        }

        /// <summary>
        ///     Gets whether any table source has been added to the current object using the From or FromRaw method.
        /// </summary>
        public bool HasTable => _clause.Has<TableElement>();

        private ISqlFromBuilder AddFrom(TableElement element)
        {
            //cross join
            element.IsCross = this.HasTable;

            _clause.Add(element);

            return this;
        }

        /// <summary>
        ///     Adds a table or view (with the alias and the schema specified) to the FROM clause of the current object.
        /// </summary>
        /// <param name="table">The name of the table or view.</param>
        /// <returns>Returns the current ISqlFromBuilder object.</returns>
        public ISqlFromBuilder From(string table)
        {
            return this.From(table, null);
        }

        /// <summary>
        ///     Adds a table or view (with the alias and the schema specified) to the FROM clause of the current object.
        /// </summary>
        /// <param name="table">The name of the table or view.</param>
        /// <param name="alias">The alias of the table or view.</param>
        /// <returns>Returns the current ISqlFromBuilder object.</returns>
        public ISqlFromBuilder From(string table, string alias)
        {
            return this.From(table, alias, null);
        }

        /// <summary>
        ///     Adds a table or view (with the alias and the schema specified) to the FROM clause of the current object.
        /// </summary>
        /// <param name="table">The name of the table or view.</param>
        /// <param name="alias">The alias of the table or view.</param>
        /// <param name="schema">The schema of the table or view.</param>
        /// <returns>Returns the current ISqlFromBuilder object.</returns>
        public ISqlFromBuilder From(string table, string alias, string schema)
        {
            return this.AddFrom(new TableElement(schema, table, alias));
        }

        /// <summary>
        ///     Adds a derived table (specified by another ISqlBuilder object and an alias) to the FROM clause of the current object.
        /// </summary>
        /// <param name="sqlBuilder">An ISqlBuilder object which represents a derived table.</param>
        /// <param name="alias">The alias of the derived table.</param>
        /// <returns>Returns the current ISqlFromBuilder object.</returns>
        public ISqlFromBuilder From(ISqlBuilder sqlBuilder, string alias)
        {
            var builder = (SqlBuilderBase)sqlBuilder;

            _clause.AddParameters(builder.Statement.Parameters);

            return this.AddFrom(new TableElement(builder.Statement, alias));
        }

        /// <summary>
        ///     Adds a table source (table, view, or derived table), specified by a raw SQL, to the FROM clause 
        ///     of the current object, and specifies an alias for this table source.
        /// </summary>
        /// <param name="fromClause">
        ///     A string of raw SQL which is the table source (table, view, or derived table). 
        ///     Actually, you can write any raw SQL if the SQL statement is valid.
        /// </param>
        /// <returns>Returns the current ISqlFromBuilder object.</returns>
        public ISqlFromBuilder FromRaw(string fromClause)
        {
            return this.FromRaw(fromClause, "");
        }

        /// <summary>
        ///     Adds a table source (table, view, or derived table), specified by a raw SQL, to the FROM clause 
        ///     of the current object, and specifies an alias for this table source.
        /// </summary>
        /// <param name="fromClause">
        ///     A string of raw SQL which is the table source (table, view, or derived table). 
        ///     Actually, you can write any raw SQL if the SQL statement is valid.
        /// </param>
        /// <param name="builderAlias">he alias for the table source.</param>
        /// <returns>Returns the current ISqlFromBuilder object.</returns>
        public ISqlFromBuilder FromRaw(string fromClause, string builderAlias)
        {
            return this.AddFrom(new TableElement(fromClause, builderAlias));
        }

        /// <summary>
        ///     Gets the alias of a table source that has been added to the current object using the From or FromRaw method.
        /// </summary>
        /// <param name="tableName">The name of a table source that has been added to the current object.</param>
        /// <returns>
        ///     Returns the alias of the specified table source if the table source exists.
        ///     Returns null if the table source does not exist.
        /// </returns>
        public string GetTableAlias(string tableName)
        {
            if (_clause.TryGet(m => tableName.Equals(m.Table), out TableElement element))
            {
                return element.Alias;
            }

            return null;
        }

        /// <summary>
        ///     Returns an ISqlJoinOnAndOr object which represents an object used to build the JOIN clause of a SQL statement.
        /// </summary>
        /// <returns>
        ///     Returns the ISqlJoinOnAndOr object which can be used to build the JOIN clause of a SQL statement 
        ///     (e.g., adding search conditions to the JOIN clause).
        /// </returns>
        public ISqlJoinOnAndOr AsJoin()
        {
            return (SqlJoinBuilder)_join;
        }

        #region Join
        public ISqlJoinOnBuilder Join(string table)
        {
            return _join.Join(table);
        }

        public ISqlJoinOnBuilder Join(string table, string alias)
        {
            return _join.Join(table, alias);
        }

        public ISqlJoinOnBuilder Join(string table, string alias, string schema)
        {
            return _join.Join(table, alias, schema);
        }

        public ISqlJoinOnBuilder JoinRaw(string joinClause)
        {
            return _join.JoinRaw(joinClause);
        }

        public ISqlJoinOnBuilder Join(ISqlBuilder sqlBuilder, string alias)
        {
            return _join.Join(sqlBuilder, alias);
        }

        public ISqlJoinOnBuilder InnerJoin(string table)
        {
            return _join.InnerJoin(table);
        }

        public ISqlJoinOnBuilder InnerJoin(string table, string alias)
        {
            return _join.InnerJoin(table, alias);
        }

        public ISqlJoinOnBuilder InnerJoin(string table, string alias, string schema)
        {
            return _join.InnerJoin(table, alias, schema);
        }

        public ISqlJoinOnBuilder InnerJoinRaw(string joinClause)
        {
            return _join.InnerJoinRaw(joinClause);
        }

        public ISqlJoinOnBuilder InnerJoin(ISqlBuilder sqlBuilder, string alias)
        {
            return _join.InnerJoin(sqlBuilder, alias);
        }

        public ISqlJoinOnBuilder LeftJoin(string table)
        {
            return _join.LeftJoin(table);
        }

        public ISqlJoinOnBuilder LeftJoin(string table, string alias)
        {
            return _join.LeftJoin(table, alias);
        }

        public ISqlJoinOnBuilder LeftJoin(string table, string alias, string schema)
        {
            return _join.LeftJoin(table, alias, schema);
        }

        public ISqlJoinOnBuilder LeftJoinRaw(string joinClause)
        {
            return _join.LeftJoinRaw(joinClause);
        }

        public ISqlJoinOnBuilder LeftJoin(ISqlBuilder sqlBuilder, string alias)
        {
            return _join.LeftJoin(sqlBuilder, alias);
        }

        public ISqlJoinOnBuilder RightJoin(string table)
        {
            return _join.RightJoin(table);
        }

        public ISqlJoinOnBuilder RightJoin(string table, string alias)
        {
            return _join.RightJoin(table, alias);
        }

        public ISqlJoinOnBuilder RightJoin(string table, string alias, string schema)
        {
            return _join.RightJoin(table, alias, schema);
        }

        public ISqlJoinOnBuilder RightJoinRaw(string joinClause)
        {
            return _join.RightJoinRaw(joinClause);
        }

        public ISqlJoinOnBuilder RightJoin(ISqlBuilder sqlBuilder, string alias)
        {
            return _join.RightJoin(sqlBuilder, alias);
        }

        public ISqlJoinOnBuilder FullJoin(string table)
        {
            return _join.FullJoin(table);
        }

        public ISqlJoinOnBuilder FullJoin(string table, string alias)
        {
            return _join.FullJoin(table, alias);
        }

        public ISqlJoinOnBuilder FullJoin(string table, string alias, string schema)
        {
            return _join.FullJoin(table, alias, schema);
        }

        public ISqlJoinOnBuilder FullJoinRaw(string joinClause)
        {
            return _join.FullJoinRaw(joinClause);
        }

        public ISqlJoinOnBuilder FullJoin(ISqlBuilder sqlBuilder, string alias)
        {
            return _join.FullJoin(sqlBuilder, alias);
        }

        public SqlJoinType? GetJoinType(string tableName)
        {
            return _join.GetJoinType(tableName);
        }

        public ISqlJoinOnBuilder JoinRaw(string joinClause, string builderAlias)
        {
            return _join.JoinRaw(joinClause, builderAlias);
        }

        public ISqlJoinOnBuilder InnerJoinRaw(string joinClause, string builderAlias)
        {
            return _join.InnerJoinRaw(joinClause, builderAlias);
        }

        public ISqlJoinOnBuilder LeftJoinRaw(string joinClause, string builderAlias)
        {
            return _join.LeftJoinRaw(joinClause, builderAlias);
        }

        public ISqlJoinOnBuilder RightJoinRaw(string joinClause, string builderAlias)
        {
            return _join.RightJoinRaw(joinClause, builderAlias);
        }

        public ISqlJoinOnBuilder FullJoinRaw(string joinClause, string builderAlias)
        {
            return _join.FullJoinRaw(joinClause, builderAlias);
        }
        #endregion

        #region Where
        public bool HasWhere => _join.HasWhere;

        public bool HasJoin => _join.HasJoin;

        public bool HasOn => _join.HasOn;

        public ISqlWhereAndOr Where(string left, ISqlParameter parameter)
        {
            return _join.Where(left, parameter);
        }

        public ISqlWhereAndOr Where(string left, SqlBinaryOperator sqlOperator, ISqlParameter parameter)
        {
            return _join.Where(left, sqlOperator, parameter);
        }

        public ISqlWhereAndOr Where(string left, string right)
        {
            return _join.Where(left, right);
        }

        public ISqlWhereAndOr Where(string left, SqlBinaryOperator sqlOperator, string right)
        {
            return _join.Where(left, sqlOperator, right);
        }

        public ISqlWhereAndOr Where(string left, ISqlBuilder sqlBuilder)
        {
            return _join.Where(left, sqlBuilder);
        }

        public ISqlWhereAndOr Where(string left, SqlBinaryOperator sqlOperator, ISqlBuilder sqlBuilder)
        {
            return _join.Where(left, sqlOperator, sqlBuilder);
        }

        public ISqlWhereAndOr Where(ISqlWhereCondition condition)
        {
            return _join.Where(condition);
        }

        public ISqlWhereAndOr WhereRaw(string whereClause, params ISqlParameter[] parameters)
        {
            return _join.WhereRaw(whereClause, parameters);
        }

        public ISqlWhereAndOr WhereValue(string left, object value)
        {
            return _join.WhereValue(left, value);
        }

        public ISqlWhereAndOr WhereValue(string left, SqlBinaryOperator sqlOperator, object value)
        {
            return _join.WhereValue(left, sqlOperator, value);
        }

        public ISqlWhereAndOr Where(ISqlWhereCondition condition, string whereName)
        {
            return _join.Where(condition, whereName);
        }

        public ISqlWhereAndOr WhereRaw(string whereClause, string whereName, params ISqlParameter[] parameters)
        {
            return _join.WhereRaw(whereClause, whereName, parameters);
        }

        public ISqlWhereAndOr AsWhere()
        {
            return _join.AsWhere();
        }

        public ISqlWhereAndOr WhereIsNull(string left)
        {
            return _join.WhereIsNull(left);
        }

        public ISqlWhereAndOr WhereIsNotNull(string left)
        {
            return _join.WhereIsNotNull(left);
        }

        public ISqlWhereAndOr WhereExists(ISqlBuilder sqlBuilder)
        {
            return _join.WhereExists(sqlBuilder);
        }

        public ISqlWhereAndOr WhereNotExists(ISqlBuilder sqlBuilder)
        {
            return _join.WhereNotExists(sqlBuilder);
        }

        #endregion

        public bool RemoveWhere()
        {
            return _join.RemoveWhere();
        }

        public bool RemoveWhere(string whereName)
        {
            return _join.RemoveWhere(whereName);
        }

        #region GroupBy
        public ISqlGroupBuilder GroupBy(params string[] groupExpressions)
        {
            return _join.GroupBy(groupExpressions);
        }

        public ISqlGroupBuilder GroupBy(string groupClause)
        {
            return _join.GroupBy(groupClause);
        }

        public ISqlGroupBuilder AsGroupBy()
        {
            return _join.AsGroupBy();
        }
        #endregion

        #region Having
        public ISqlHavingAndOr HavingValue(string left, object value)
        {
            return _join.HavingValue(left, value);
        }

        public ISqlHavingAndOr HavingValue(string left, SqlBinaryOperator sqlOperator, object value)
        {
            return _join.HavingValue(left, sqlOperator, value);
        }

        public ISqlHavingAndOr Having(string left, ISqlParameter parameter)
        {
            return _join.Having(left, parameter);
        }

        public ISqlHavingAndOr Having(string left, SqlBinaryOperator sqlOperator, ISqlParameter parameter)
        {
            return _join.Having(left, sqlOperator, parameter);
        }

        public ISqlHavingAndOr Having(string left, string right)
        {
            return _join.Having(left, right);
        }

        public ISqlHavingAndOr Having(string left, SqlBinaryOperator sqlOperator, string right)
        {
            return _join.Having(left, sqlOperator, right);
        }

        public ISqlHavingAndOr Having(string left, ISqlBuilder sqlBuilder)
        {
            return _join.Having(left, sqlBuilder);
        }

        public ISqlHavingAndOr Having(string left, SqlBinaryOperator sqlOperator, ISqlBuilder sqlBuilder)
        {
            return _join.Having(left, sqlOperator, sqlBuilder);
        }

        public ISqlHavingAndOr Having(ISqlHavingCondition condition)
        {
            return _join.Having(condition);
        }

        public ISqlHavingAndOr HavingRaw(string havingClause, params ISqlParameter[] parameters)
        {
            return _join.HavingRaw(havingClause, parameters);
        }

        public ISqlHavingAndOr Having(ISqlHavingCondition condition, string havingName)
        {
            return _join.Having(condition, havingName);
        }

        public ISqlHavingAndOr HavingRaw(string havingClause, string havingName, params ISqlParameter[] parameters)
        {
            return _join.HavingRaw(havingClause, havingName, parameters);
        }

        public ISqlHavingAndOr AsHaving()
        {
            return _join.AsHaving();
        }
        #endregion

        #region OrderBy
        public ISqlOrderThenBuilder OrderByAscending(params string[] orderExpressions)
        {
            return _join.OrderByAscending(orderExpressions);
        }

        public ISqlOrderThenBuilder OrderByDescending(params string[] orderExpressions)
        {
            return _join.OrderByDescending(orderExpressions);
        }

        public ISqlOrderThenBuilder OrderByRaw(string orderClause)
        {
            return _join.OrderByRaw(orderClause);
        }

        public ISqlOrderThenBuilder OrderByRaw(string orderClause, string alias)
        {
            return _join.OrderByRaw(orderClause, alias);
        }

        public ISqlOrderThenBuilder AsOrderBy()
        {
            return _join.AsOrderBy();
        }
        #endregion
    }
}
